﻿// MIT License
// 开源地址：https://gitee.com/co1024/AbcMvc
// Copyright (c) 2021-2023 1024
// Abc.Mvc=Furion+EF+SqlSugar+Pear layui admin.

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;

namespace Abc.Utils;

/*
 asp.net core程序部署在centos7（下面的解决方案，其他系统都能使用，这里只是我自己部署在centos7），使用服务器jexus进行部署，AppHost模式。
因为请求是由jexus进行了转发的，所以asp.net zero获取的ip永远都是127.0.0.1.。
解决方案：
使用由Jexus作者宇内流云提供的JwsIntegration替换IISIntegration
，它改变默认从请求头获取ip的规则，改为由 “X-Original-For”获取远程ip（
经测试 使用"X-Real-IP"也能获取）。
https://cloud.tencent.com/developer/article/1151164
 */

/// <summary>
/// 用于处理客户IP地址、端口的HostBuilder中间件
/// </summary>
public static class WebHostBuilderJexusExtensions
{

    /// <summary>
    /// 启用JexusIntegration中间件
    /// </summary>
    /// <param name="hostBuilder"></param>
    /// <returns></returns>
    public static IWebHostBuilder UseJexusIntegration(this IWebHostBuilder hostBuilder)
    {
        if (hostBuilder == null)
        {
            throw new ArgumentNullException(nameof(hostBuilder));
        }

        // 检查是否已经加载过了
        if (hostBuilder.GetSetting(nameof(UseJexusIntegration)) != null)
        {
            return hostBuilder;
        }


        // 设置已加载标记，防止重复加载
        hostBuilder.UseSetting(nameof(UseJexusIntegration), true.ToString());


        // 添加configure处理
        hostBuilder.ConfigureServices(services =>
        {
            services.AddSingleton<IStartupFilter>(new JwsSetupFilter());
        });


        return hostBuilder;
    }

}

class JwsSetupFilter : IStartupFilter
{
    public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
    {
        return app =>
        {
            app.UseMiddleware<JexusMiddleware>();
            next(app);
        };
    }
}


class JexusMiddleware
{
    readonly RequestDelegate _next;
    public JexusMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IOptions<IISOptions> options)
    {
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        var headers = httpContext.Request.Headers;

        try
        {
            if (headers != null && headers.ContainsKey("X-Original-For"))
            {
                var ipaddAdndPort = headers["X-Original-For"].ToArray()[0];
                var dot = ipaddAdndPort.IndexOf(":", StringComparison.Ordinal);
                var ip = ipaddAdndPort;
                var port = 0;
                if (dot > 0)
                {
                    ip = ipaddAdndPort.Substring(0, dot);
                    port = int.Parse(ipaddAdndPort.Substring(dot + 1));
                }

                httpContext.Connection.RemoteIpAddress = System.Net.IPAddress.Parse(ip);
                if (port != 0) httpContext.Connection.RemotePort = port;
            }
        }
        finally
        {
            await _next(httpContext);
        }

    }

}
